package server.core;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import server.sat.SATProblemManager;

/**
 * The Server class defines a program that sets up a problem solver server. It
 * prompts the user for the MySQL URL, username, password, and the port number
 * to listen for active connections on.
 * 
 * @author Clayton Edward Taylor
 */
public class Server {
	
	private static String url;
	private static String username;
	private static String password;
	private static int portNumber;

	/**
	 * The entry point for the program defined by Server. 
	 * 
	 * @param args - Server ignores the command-line arguments
	 * @see          Server
	 */
	public static void main(String args[]) 
			throws IOException, ClassNotFoundException, SQLException {
		
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader br = new BufferedReader(isr);
		readParameters(br);
		SocketWatcher watcher = setupServer();
		handleCommands(br);
		System.out.println("Shutting down server");
		watcher.stopWatcher();
	}
	
	/**
	 * Prompts user for the MySQL database URL, username, password, and the
	 * port number to listen for connections on, storing these parameters in 
	 * the appropriate static variables.
	 * 
	 * @param br - The user's input
	 * @throws     IOException If an I/O error occurs
	 * @see        Server
	 */
	private static void readParameters(BufferedReader br) 
			throws IOException {
		
		System.out.println("Please input the following parameters\n");
		System.out.print("MySQL database URL: ");
		url = br.readLine();
		System.out.print("MySQL database username: ");
		username = br.readLine();
		System.out.print("MySQL database password: ");
		password = br.readLine();
		System.out.print("Port number: ");
		portNumber = Integer.parseInt(br.readLine());
		System.out.print("\n");
	}
	
	/**
	 * Sets up the server by creating a new {@link Database} object, adding
	 * {@link SATProblemManager} to {@link Managers}, and starting a new
	 * {@link SocketWatcher}.
	 * 
	 * @return An active SocketWatcher
	 * @throws ClassNotFoundException If the MySQL driver is not found
	 * @throws SQLException If the connection to the MySQL database cannot
	 *         be established
	 * @see    Server
	 */
	private static SocketWatcher setupServer() 
			throws ClassNotFoundException, SQLException {
		Database db = new Database(url, username, password);
		Managers.setManager("sat", new SATProblemManager(db));
		ExecutorService threadPool = Executors.newCachedThreadPool();
		SocketWatcher watcher = new SocketWatcher(portNumber, threadPool);
		new Thread(watcher).start();
		return watcher;
	}

	/**
	 * Handles commands that the user types in. The currently supported
	 * commands are:
	 * <ul>
	 * <li><b>clear type</b> - Clears the cache for the specified problem type</li> 
	 * <li><b>quit</b></li> - Shuts down the server
	 * </ul>
	 * 
	 * @param br - The user's input
	 * @see        Server
	 */
	private static void handleCommands(BufferedReader br) {

		try {
			String line;
			System.out.print("> ");
			while ((line = br.readLine()) != null) {
				String[] tokens = line.split(" ");
				if (tokens[0].equals("quit")) {
					break;
				} else if (tokens[0].equals("clear")) {
					clearCommand(tokens);
				} else {
					System.out.println("Unrecognized command");
				}
				System.out.print("> ");
			}
		} catch (IOException e) {
			System.err.println(e.getMessage());
			return;
		}
	}
	
	/**
	 * Handles the clear command.
	 * 
	 * @param tokens - A line of the user's input tokenized into words
	 * @see            Server
	 */
	private static void clearCommand(String[] tokens) {
		
		if (tokens.length != 2) {
			System.out.println("Invalid usage of command (expected format: clear type)");
			return;
		}
		ProblemManager pm = Managers.getManager(tokens[1]);
		if (pm == null) {
			System.out.println("No problem manager for the specified problem type");
			return;
		}
		pm.clearCache();
		System.out.println("Cleared cache for " + tokens[1]);
	}
}
